package com.dux.commons.concurrent;

import java.lang.management.LockInfo;
import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

import org.apache.log4j.Logger;

import com.dux.commons.utils.ExitCode;


/**
 * 死锁检测
 * @author 杜祥
 * @create 2013-6-9
 */
public class DeadLockDetector extends Thread 
{
	private final static Logger log = Logger.getLogger(DeadLockDetector.class);
	
	public static final byte NOTHING = 0;
	
	public static final byte RESTART = 1;
	
	/**
	 * 多久检查一次死锁
	 */
	private final int sleepTime;
	
	/**
	 * java虚拟机线程管理接口
	 */
	private final ThreadMXBean tmx;
	
	/**
	 * 发现以后执行的操作
	 */
	private final byte doWhenDL;
	
	
	/**
	 * 构造一个新的死锁检测类，并设置检测时间
	 * @param sleepTime
	 * @param doWhenDL
	 */
	public DeadLockDetector(int sleepTime, byte doWhenDL)
	{
		super("DeadLockDetector");
		
		this.sleepTime = sleepTime;
		
		this.tmx = ManagementFactory.getThreadMXBean();
		
		this.doWhenDL = doWhenDL;
	}


	@Override
	public void run() 
	{
		boolean deadLock = false;
		
		while(!deadLock)
		{
			try
			{
				long[] ids = this.tmx.findDeadlockedThreads();
				
				if(ids != null)
				{
					deadLock = true;
					
					ThreadInfo[] tis = this.tmx.getThreadInfo(ids, Boolean.TRUE, Boolean.TRUE);
					
					String info = "发现死锁！\n";
					
					for(ThreadInfo ti : tis)
					{
						info += ti.toString();
					}
					
					for(ThreadInfo ti : tis)
					{
						LockInfo[] locks = ti.getLockedSynchronizers();
						MonitorInfo[] monitors = ti.getLockedMonitors();
						if(locks.length == 0 && monitors.length == 0)
						{
							continue;
						}
						ThreadInfo dl = ti;
						info += "Java-level deadlock:\n";
						info += "\t" + dl.getThreadName() + " is waiting to lock " + dl.getLockInfo().toString()
							+ " which is held by " + dl.getLockOwnerName() + "\n";
						while ((dl = tmx.getThreadInfo(new long[] { dl.getLockOwnerId() }, true, true)[0])
							.getThreadId() != ti.getThreadId())
						{
							info += "\t" + dl.getThreadName() + " is waiting to lock " + dl.getLockInfo().toString()
								+ " which is held by " + dl.getLockOwnerName() + "\n";
						}
					}
					log.warn(info);

					if (doWhenDL == RESTART)
					{
						System.exit(ExitCode.CODE_RESTART);
					}
				}
				Thread.sleep(sleepTime);
			} 
			catch (Exception e)
			{
				log.warn("DeadLockDetector: " + e, e);
			}
		}
	}
	
	
}
